# partially based on django-tremenus code written by Julien Phalip
# (http://code.google.com/p/django-treemenus/)
#
# Copyright (c) 2008, Julien Phalip
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
#
#     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
#     * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

from itertools import chain
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse
from django.db import models
from django.utils import translation
from django.utils.translation import ugettext, ugettext_lazy as _
from djwheel.adminutils import create_preview_field, create_link_field
from djwheel.langutils import cache_method_value, merge_attrs
from djwheel.utils import trunc_text, load_class

import appsettings

settings = merge_attrs(settings, appsettings)


class DefaultMenuItem(models.Model):
    is_visible = models.BooleanField(_('is visible'), default=True)
    parent = models.ForeignKey('self', verbose_name=_('parent'), null=True, blank=True)
    level = models.IntegerField(_('level'), default=0, editable=False)
    order = models.IntegerField(_('order'), default=0, editable=False)
    menu = models.ForeignKey('Menu', related_name='contained_items',
        verbose_name=_('menu'), null=True, blank=True, editable=False)
    title = models.CharField(_('title'), max_length=255,
        help_text=_('Enter "---" for splitter.'))
    url = models.CharField(_('URL'), max_length=255, blank=True,
        help_text=_('Starts with "/" page url for simple pages with default template.'))
    named_url = models.CharField(_('named URL'), max_length=255, blank=True,
        help_text=_('Page name in urls.py. For advanced pages only.'))

    def is_splitter(self):
        return self.title.startswith('---')

    @cache_method_value
    def get_url(self):
        """Return url from url or named url fields"""
        return self.named_url and reverse(self.named_url) or self.url

    def adm_get_url(self):
        """Url representaion for admin view"""
        if self.named_url:
            return _('%s [from urls.py]') % self.get_url()
        else:
            return self.get_url()

    def get_self_or_child_url(self):
        """Self url or first url from children"""
        url = self.get_url()
        if url:
            return url
        elif self.has_children():
            return self.children()[0].get_self_or_child_url()
        else:
            return ''

    def __unicode__(self):
        return self.title

    def title_with_spacer(self):
        spacer = self.level and '&nbsp;' * 5 * self.level + '|-&nbsp;' or ''
        if self.is_visible:
            return spacer + trunc_text(self.title, 40)
        else:
            return spacer + ugettext('[hidden] ') + trunc_text(self.title, 40)

    def __eq__(self, other):
        return self.__class__ == other.__class__ and self.id == other.id

    def save(self, *args, **kwargs):
        from utils import clean_order

        self.url = self.url.strip()
        self.named_url = self.named_url.strip()

        # Calculate level
        old_level = self.level
        if self.parent:
            self.level = self.parent.level + 1
        else:
            self.level = 0

        if self.pk:
            new_parent = self.parent
            old_parent = MenuItem.objects.get(pk=self.pk).parent
            if old_parent != new_parent:
                #If so, we need to recalculate the new order for the item and its siblings (both old and new ones).
                if new_parent:
                    clean_order(new_parent.children()) # Clean order for new siblings
                    self.order = new_parent.children().count()
                super(DefaultMenuItem, self).save(*args, **kwargs) # Save menu item in DB. It has now officially changed parent.
                if old_parent:
                    clean_order(old_parent.children()) # Clean order for old siblings
            else:
                super(DefaultMenuItem, self).save(*args, **kwargs) # Save menu item in DB

        else: # Saving the menu item for the first time (i.e creating the object)
            if not self.has_siblings():
                # No siblings - initial order is 0.
                self.order = 0
            else:
                # Has siblings - initial order is highest sibling order plus 1.
                siblings = self.siblings().order_by('-order')
                self.order = siblings[0].order + 1
            super(DefaultMenuItem, self).save(*args, **kwargs)

        # If level has changed, force children to refresh their own level
        if old_level != self.level:
            for child in self.children():
                child.save() # Just saving is enough, it'll refresh its level correctly.

    def delete(self):
        from utils import clean_order
        old_parent = self.parent
        super(DefaultMenuItem, self).delete()
        if old_parent:
            clean_order(old_parent.children())

    def get_flattened(self):
        flat_structure = [self]
        for child in self.children():
            flat_structure = chain(flat_structure, child.get_flattened())
        return flat_structure

    def siblings(self):
        if not self.parent:
            return MenuItem.objects.none()
        else:
            if not self.pk: # If menu item not yet been saved in DB (i.e does not have a pk yet)
                return self.parent.children()
            else:
                return self.parent.children().exclude(pk=self.pk)

    def has_siblings(self):
        return self.siblings().count() > 0

    def children(self):
        _children = MenuItem.objects.filter(parent=self).order_by('order',)
        for child in _children:
            child.parent = self # Hack to avoid unnecessary DB queries further down the track.
        return _children

    def has_children(self):
        return self.children().count() > 0

    def get_content(self):
        try:
            c = Content.objects.get(menu_item=self)
        except Content.DoesNotExist:
            c = Content(menu_item=self)
            c.save()
        return c

    class Meta:
        abstract = True

#CurrentMenuItem = settings.CONTENTTREE_MENU_ITEM_MODEL and \
#    load_class(settings.CONTENTTREE_MENU_ITEM_MODEL) or DefaultMenuItem
#
#class MenuItem(CurrentMenuItem):
#    class Meta(CurrentMenuItem.Meta):
#        abstract = False

if settings.CONTENTTREE_MENU_ITEM_MODEL:
    UserMenuItem = load_class(settings.CONTENTTREE_MENU_ITEM_MODEL)
    class MenuItem(DefaultMenuItem, UserMenuItem):
        class Meta(DefaultMenuItem.Meta, UserMenuItem.Meta):
            abstract = False
            verbose_name = _('menu item')
            verbose_name_plural = _('menu items')
else:
    class MenuItem(DefaultMenuItem):
        class Meta(DefaultMenuItem.Meta):
            abstract = False
            verbose_name = _('menu item')
            verbose_name_plural = _('menu items')


class Menu(models.Model):
    name = models.CharField(_('name'), max_length=50)
    description =  models.CharField(_('description'), max_length=255)
    root_item = models.ForeignKey(MenuItem, related_name='is_root_item_of',
        verbose_name=_('root item'), null=True, blank=True, editable=False)

    @classmethod
    def get(cls, name):
        #return cls.objects.get(name=name, lang=translation.get_language())
        return cls.objects.get(name=name)

    def save(self, *args, **kwargs):
        if not self.root_item:
            root_item = MenuItem()
            root_item.title = ugettext('root')
            if not self.pk: # If creating a new object (i.e does not have a pk yet)
                super(Menu, self).save(*args, **kwargs) # Save, so that it gets a pk
            root_item.menu = self
            root_item.save() # Save, so that it gets a pk
            self.root_item = root_item
        super(Menu, self).save(*args, **kwargs)

    def delete(self):
        if self.root_item is not None:
            self.root_item.delete()
        super(Menu, self).delete()

    def __unicode__(self):
        return '%s (%s)' % (self.description, self.name)

    class Meta:
        verbose_name = _('menu')
        verbose_name_plural = _('menus')


class DefaultContent(models.Model):
    menu_item = models.OneToOneField(MenuItem, related_name='extension', editable=False)
    title = models.CharField(_('page title'), max_length=255, blank=True,
        help_text=_('If not equals to title'))
    content  = models.TextField(_('Content'), blank=True)
    meta_keywords = models.TextField(_('<meta keywords="">'), blank=True)
    meta_description = models.TextField(_('<meta description="">'), blank=True)

    def get_title(self):
        return self.title or self.menu_item.title

    class Meta:
        abstract = True

    def __unicode__(self):
        return unicode(self.menu_item.id)


if settings.CONTENTTREE_CONTENT_MODEL:
    UserContent = load_class(settings.CONTENTTREE_CONTENT_MODEL)
    class Content(DefaultContent, UserContent):
        class Meta(DefaultContent.Meta, UserContent.Meta):
            abstract = False
            verbose_name = _('content')
            verbose_name_plural = _('contents')
else:
    class Content(DefaultContent):
        class Meta(DefaultContent.Meta):
            abstract = False
            verbose_name = _('content')
            verbose_name_plural = _('contents')

class AbstractFreeTexts(models.Model):
    name = models.CharField(_('name'), max_length=40)
    description = models.CharField(_('description'), max_length=120)

    def __unicode__(self):
        return self.name

    class Meta:
        abstract = True


class FreeContent(AbstractFreeTexts):
    value = models.TextField(_('value'))

    def adm_value(self):
        return trunc_text(self.value, 200)
    adm_value.short_description = value.verbose_name

    class Meta:
        verbose_name = _('free content')
        verbose_name_plural = _('free contents')


class Legend(AbstractFreeTexts):
    value = models.TextField(_('value'))

    class Meta:
        verbose_name = _('legend')
        verbose_name_plural = _('legends')


class Images(models.Model):
    image = models.ImageField(_('Image'), upload_to=settings.CONTENTTREE_IMAGES_UPLOAD_TO)
    description = models.CharField(_('Description'), max_length=255)
    preview_image = create_preview_field(_('Preview'), 'image', 'description')
    adm_preview_image = create_preview_field(_('Preview'), 'image', 'description',
        max_size=(800, 1000))

    def adm_path(self):
        return self.image.url
    adm_path.short_description = _('image path')

    def adm_code(self):
        return unicode(self.preview_image())
    adm_code.short_description = _('HTML')

    class Meta:
        verbose_name = _('image')
        verbose_name_plural = _('images')

    def __unicode__(self):
        return self.description


class Files(models.Model):
    file = models.FileField(_('File'), upload_to=settings.CONTENTTREE_FILES_UPLOAD_TO)
    description = models.CharField(_('Description'), max_length=255)
    link = create_link_field(_('Link'), 'file', 'description')

    def adm_code(self):
        return u'<a href="%s">%s</a>' % (self.link(), self.description)
    adm_code.short_description = _('code')

    def __unicode__(self):
        return self.description

    class Meta:
        verbose_name = _('file')
        verbose_name_plural = _('files')

